#include <stdio.h>
#include <kewinternal.h>
#include "myglib.h"

#define SOURCE_FILE_NAME "Map"

#define value(OBJECT) ((GHashTable *)((OBJECT)->context[0]))
#define map_reader_value(OBJECT) ((OBJECT)->context[0])


METHOD(_Append)
{
  GHashTable *copy = g_hash_table_new((GHashFunc) Kew__HashFunction,
                                      (GCompareFunc) Kew__EqualFunction);
  g_hash_table_insert_all(copy, value(receiver));
  g_hash_table_insert(copy, (gpointer) args[0], (gpointer) args[1]);
  results[0] = KewNewMap(copy);
}

METHOD(Size)
{
  results[0] = KewNewInt(g_hash_table_size(value(receiver)));
}

METHOD(At)
{
  KewObject *message;
  KewObject *element = (KewObject *) g_hash_table_lookup(value(receiver),
                                                         args[0]);
  if (element != NULL) {
    results[0] = element;
    return;
  }

  message = KewSend1(KewNewString("Missing key: "), ",", args[0]);
  results[0] = KewSend1(KewMissingKey, "raise", message);
}

METHOD(Includes)
{
  results[0] = KewNewBool(g_hash_table_lookup(value(receiver),args[0]) != NULL);
}

static KewObject *
Collect_Iterator(KewObject *key, KewObject *value, KewObject *block)
{
  return KewSend2(block, "do", key, value);
}

METHOD(Collect)
{
  results[0] = KewNewMap(g_hash_table_collect(value(receiver),
                                        (GHCollectFunc) Collect_Iterator,
                                        args[0],
                                        (GHashFunc) Kew__HashFunction,
                                        (GCompareFunc) Kew__EqualFunction));
}

METHOD(Map)
{
  results[0] = receiver;
}

METHOD(map_reader_read)
{
  results[0] = KewSend0(map_reader_value(receiver), "read");
}

METHOD(map_reader_empty)
{
  results[0] = KewSend0(map_reader_value(receiver), "empty");
}

METHOD(map_reader_reader)
{
  results[0] = receiver;
}

static KewObjectType map_reader_type = {
  SOURCE_FILE_NAME, 0, 1,
  {
    {"reader",map_reader_reader,0,1},
    {"read",map_reader_read,0,1},
    {"empty",map_reader_empty,0,1},
    {NULL,NULL,0,0}
  }
};

static void
reader_callback(KewObject *key, KewObject *value, KewObject **value_list)
{
  *value_list = KewSend1(*value_list, "&", value);
}

METHOD(Reader)
{
  KewObject *value_list = KewEmptyList;
  g_hash_table_foreach(value(receiver), (GHFunc) reader_callback, (gpointer) &value_list);

  results[0] = KewNewObject(&map_reader_type);
  map_reader_value(results[0]) = KewSend0(value_list, "reader");
}

static void
Finalize(KewObject *object)
{
  g_hash_table_destroy(value(object));
}


static KewObjectType kew_map_type = {
  SOURCE_FILE_NAME, __LINE__,
  1,
  {
    {"=", Kew__GenericEqual, 1, 1},
    {"hash", Kew__GenericHash, 0, 1},
    {"&", _Append, 2, 1},
    {"size", Size, 0, 1},
    {"collect", Collect, 1, 1},
    {"at", At, 1, 1},
    {"includes", Includes, 1, 1},
    {"map", Map, 0, 1},
    {"reader", Reader, 0, 1},
    {NULL, NULL, 0, 0}
  }
};

KewObject *
KewNewMap(GHashTable *hashtable)
{
  KewObject *result = KewNewObject(&kew_map_type);
  value(result) = hashtable;
  return result;
}


void
Kew__InitMap(void)
{
  KewMap = KewNewMap(g_hash_table_new((GHashFunc) Kew__HashFunction,
                                      (GCompareFunc) Kew__EqualFunction));
}
